package com.oritsh.dicom.utils;

import com.oritsh.dicom.service.InstanceUIDService;
import com.oritsh.dicom.service.InstanceUIDSrvPixelmedImpl;
import org.dcm4che.data.Attributes;
import org.dcm4che.data.Tag;
import org.dcm4che.data.UID;
import org.dcm4che.data.VR;
import org.dcm4che.imageio.plugins.dcm.DicomImageReaderSpi;
import org.dcm4che.io.DicomEncodingOptions;
import org.dcm4che.io.DicomInputStream;
import org.dcm4che.io.DicomOutputStream;

import javax.imageio.*;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.*;
import java.util.*;
import java.util.List;

import static com.oritsh.dicom.utils.DataSetHelper.putValueToDataSet;

/**
 * Created with IntelliJ IDEA.
 * User: zarra
 * Date: 14-2-10
 * Time: 下午9:47
 * Copyright Shanghai Orient Rain Information Technology Co.,Ltd.
 */
public class ImageHelper {

    static public BufferedImage dcm2pic(File file) throws IOException {
        InputStream inputStream = new FileInputStream(file);
        return dcm2pic(new DicomInputStream(inputStream));
    }

    static public BufferedImage dcm2pic(DicomInputStream inputStream) throws IOException {
        try (ImageInputStream iis = ImageIO.createImageInputStream(inputStream)) {
            DicomImageReaderSpi spi = new DicomImageReaderSpi();
            ImageReader reader = spi.createReaderInstance();
            reader.setInput(iis);
            BufferedImage image = reader.read(0);
            return image;
        }
    }

    static public BufferedImage imageCopy(BufferedImage image){
        BufferedImage copyOfImage =
                new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics g = copyOfImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        return copyOfImage;
    }

    static public BufferedImage imageAddPatientAnnotation(BufferedImage image,Attributes datasets){
        //BufferedImage img = imageCopy(image);
        Graphics2D g2d = image.createGraphics();
        g2d.setColor(Color.RED);
        g2d.setFont(new Font("SansSerif",Font.BOLD,18));

        List<String> patientInfo = Arrays.asList(
                datasets.getString(Tag.PatientID,null),
                datasets.getString(Tag.PatientName,null),
                datasets.getString(Tag.PatientSex,null),
                datasets.getString(Tag.PatientAge,null)
                //datasets.getString(Tag.PatientBirthDate,null)
        );

        int y=20;int x = 0;

        for (String info : patientInfo){
            if (info == null)
                continue;
            else{
            	x = 10;
                //x = image.getWidth() - 18*info.length();
                g2d.drawString(info,x,y);
                y+=22;
            }
        }

        return image;
    }

    static public BufferedImage imageAddStudyAnnotation(BufferedImage image,Attributes datasets){
        //BufferedImage img = imageCopy(image);
        Graphics2D g2d = image.createGraphics();
        g2d.setColor(Color.RED);
        g2d.setFont(new Font("SansSerif",Font.BOLD,18));

        List<String> studyInfo = Arrays.asList(
                datasets.getString(Tag.AcquisitionDate,null),
                datasets.getString(Tag.AcquisitionTime,null)
        );

        int y=image.getHeight()-40;int x = 0;
        for (String info : studyInfo){
            if (info == null)
                continue;
            else{
                g2d.drawString(info,x,y);
                y+=22;
            }
        }

        return image;
    }
    
    static public BufferedImage imageAddStudyPartAnnotation(BufferedImage image,Attributes datasets){
        //BufferedImage img = imageCopy(image);
        Graphics2D g2d = image.createGraphics();
        g2d.setColor(Color.RED);
        g2d.setFont(new Font("SansSerif",Font.BOLD,18));

        List<String> studyInfo = Arrays.asList(
                datasets.getString(Tag.Modality,null),
                datasets.getString(Tag.BodyPartExamined,null)
        );

        int y=22;int x = 0;
        for (String info : studyInfo){
            if (info == null)
                continue;
            else{
            	x = image.getWidth() - 18*info.length();
                g2d.drawString(info,x,y);
                y+=22;
            }
        }

        return image;
    }

    static public BufferedImage imageAddInstitutionAnnotation(BufferedImage image,Attributes datasets){
        //BufferedImage img = imageCopy(image);
        Graphics2D g2d = image.createGraphics();
        g2d.setColor(Color.RED);
        g2d.setFont(new Font("SansSerif",Font.BOLD,18));

        List<String> institutionInfo = Arrays.asList(
                datasets.getString(Tag.InstitutionName,null),
                datasets.getString(Tag.InstitutionAddress,null)

        );

        int y=20;int x = 0;
        Collections.reverse(institutionInfo);
        //y=image.getHeight() -20;
        //x=0;
        y= image.getHeight()-40;
        for (String info : institutionInfo){
            if (info == null)
                continue;
            else{
            	x = image.getWidth() - 18*info.length();
                g2d.drawString(info,x,y);
                y-=22;
            }
        }

        return image;
    }


    static public BufferedImage imageAddText(BufferedImage image,Attributes datasets){
        BufferedImage img = imageCopy(image);
        img = imageAddPatientAnnotation(img,datasets);
        img = imageAddStudyAnnotation(img,datasets);
        img = imageAddStudyPartAnnotation(img,datasets);
        img = imageAddInstitutionAnnotation(img,datasets);
        return img;
    }

    static public BufferedImage dcm2pic(com.pixelmed.dicom.DicomInputStream dcmFile) throws IOException {
        DicomInputStream inputStream = new DicomInputStream(dcmFile);
        return dcm2pic(inputStream);
    }

    static public void saveImage(BufferedImage image, OutputStream out, String mimetype) throws IOException {
        ImageWriter imageWriter;
        try {
            imageWriter = ImageIO.getImageWritersByMIMEType(mimetype).next();
        } catch (NoSuchElementException e) {
            imageWriter = ImageIO.getImageWritersByFormatName(mimetype).next();
        }
        ImageWriteParam imageWriterParam = imageWriter.getDefaultWriteParam();
        ImageOutputStream ios = ImageIO.createImageOutputStream(out);
        imageWriter.setOutput(ios);
        imageWriter.write(null, new IIOImage(image, null, null), imageWriterParam);
        out.flush();
        ios.close();
    }

    static public void saveImage(BufferedImage image, File outFile, String mimetype) throws IOException {
        BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outFile));
        saveImage(image, outputStream, mimetype);
        outputStream.close();
    }

    private static byte[] convert2RGB(BufferedImage image){
        byte[] result = null;
        DataBufferByte buff;
        switch (image.getType()){
            case BufferedImage.TYPE_3BYTE_BGR:
                buff = (DataBufferByte) image.getData().getDataBuffer();
                result = buff.getData(0);
                for(int i=0;i<result.length;i+=3){
                    byte temp = result[i];
                    result[i] = result[i+2];
                    result[i+2] = temp;
                }
                break;
            default:
                buff = (DataBufferByte) image.getData().getDataBuffer();
                result = buff.getData(0);
        }
        return result;
    }

    static public DicomInputStream pic2dcm(BufferedImage image,Attributes dataset) throws IOException {
        
    	image = imageAddPatientAnnotation(image,dataset);
    	image = imageAddStudyAnnotation(image,dataset);
    	image = imageAddInstitutionAnnotation(image,dataset);
    	
    	int colorComponents = image.getColorModel().getNumColorComponents();
        int bitsPerPixel = image.getColorModel().getPixelSize();
        int bitsAllocated = (bitsPerPixel/colorComponents);
        int samplesPerPixel = colorComponents;

        putValueToDataSet(dataset, Tag.SpecificCharacterSet, "ISO_IR 192");
        putValueToDataSet(dataset, Tag.PhotometricInterpretation, samplesPerPixel == 3 ? "RGB" : "MONOCHROME2");
        putValueToDataSet(dataset, Tag.SamplesPerPixel, samplesPerPixel);
        putValueToDataSet(dataset,Tag.Rows,image.getHeight());
        putValueToDataSet(dataset,Tag.Columns,image.getWidth());
        putValueToDataSet(dataset,Tag.BitsAllocated,bitsAllocated);
        putValueToDataSet(dataset,Tag.BitsStored,bitsAllocated);
        putValueToDataSet(dataset,Tag.HighBit,bitsAllocated-1);
        putValueToDataSet(dataset,Tag.PixelRepresentation,0);
        putValueToDataSet(dataset,Tag.PlanarConfiguration,0);

        putValueToDataSet(dataset,Tag.SOPClassUID, UID.SecondaryCaptureImageStorage);

        byte[] bytes = convert2RGB(image);
        if (bytes != null){
            dataset.setBytes(Tag.PixelData, VR.OW,bytes);
        }

        dataset.trimToSize();

        InstanceUIDService instanceUIDService = new InstanceUIDSrvPixelmedImpl();

        String studyUID = dataset.getString(Tag.StudyInstanceUID);
        if (studyUID == null){
            studyUID = instanceUIDService.generateStudyInstanceUID();
            putValueToDataSet(dataset,Tag.StudyInstanceUID,studyUID);
        }

        String seriesUID = dataset.getString(Tag.SeriesInstanceUID);
        if (seriesUID == null){
            seriesUID = instanceUIDService.generateSeriesInstanceUID(studyUID,"1");
            putValueToDataSet(dataset,Tag.SeriesInstanceUID,seriesUID);
        }

        String sopUID = dataset.getString(Tag.SOPInstanceUID);
        if (sopUID == null){
            sopUID = instanceUIDService.generateSOPInstanceUID(studyUID,"1","1");
            putValueToDataSet(dataset,Tag.SOPInstanceUID,sopUID);
        }

        Attributes fmi = Attributes.createFileMetaInformation(
                sopUID, UID.SecondaryCaptureImageStorage , UID.ExplicitVRLittleEndian);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        DicomOutputStream dos = new DicomOutputStream(byteArrayOutputStream,UID.ExplicitVRLittleEndian);
        DicomEncodingOptions encOpts = DicomEncodingOptions.DEFAULT;
        dos.setEncodingOptions(encOpts);

        dos.writeDataset(fmi, dataset);

        dos.finish();
        dos.close();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return new DicomInputStream(byteArrayInputStream);
    }
}
